/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
*/

package org.elephantt.webby;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

import java.util.Map;
import java.io.IOException;

/**
 * A request handler for resources
 */
public class ResourceController {
  private static final Log log = LogFactory.getLog(ResourceRequestHandler.class);
  private final Class resourceClass;

  /**
   * Create an instance givin a Class that is a Resource.
   * @param resourceHandlerClass
   */
  public ResourceController(Class resourceHandlerClass) {
    if (!Resource.class.isAssignableFrom(resourceHandlerClass)) {
      throw new IllegalArgumentException(resourceHandlerClass + " is not assignable from " + Resource.class);
    }

    this.resourceClass = resourceHandlerClass;
  }

  public Responder handleGet(Request request, Response response) throws IOException {
    return createResource(request, response).get();
  }

  public Responder handlePost(Request request, Response response) throws IOException {
    return createResource(request, response).post();
  }

  public Responder handlePut(Request request, Response response) throws IOException {
    return createResource(request, response).put();
  }

  public Responder handleDelete(Request request, Response response) throws IOException {
    return createResource(request, response).delete();
  }

  public Responder handleOptions(Request request, Response response) throws IOException {
    return createResource(request, response).options();
  }

  public Responder handleHead(Request request, Response response) throws IOException {
    return createResource(request, response).head();
  }

  public Responder handleTrace(Request request, Response response) throws IOException {
    return createResource(request, response).trace();
  }

  public Responder handleConnect(Request request, Response response) throws IOException {
    return createResource(request, response).connect();
  }

  public Responder handleOther(Request request, Response response) throws IOException {
    return createResource(request, response).other();
  }

  /**
   * Creates the Resource instance and sets the request, response, and URL path parameter properties on it.
   */
  private Resource createResource(Request request, Response response) {
    try {
      if (log.isDebugEnabled()) log.debug("creating resource for request " + request);
      Resource resource = (Resource) resourceClass.newInstance();

      setOptionalProperty(resource, "request", request);
      setOptionalProperty(resource, "response", response);

      Map<String, String> urlPathParamMap = request.getUrlParameters();
      for (Map.Entry<String, String> entry : urlPathParamMap.entrySet()) {
        setOptionalProperty(resource, entry.getKey(), entry.getValue());
      }

      return resource;
    } catch (Exception e) {
      throw new RuntimeException("Could not instantiate Resource instance of type " + resourceClass, e);
    }
  }

  /**
   * Sets the specified key,value on the specified object. The setting is 'optional' -- if it fails, we
   * simply log a warning and continue.
   */
  private static void setOptionalProperty(Object object, String key, Object value) {
    try {
      PropertyUtils.setSimpleProperty(object, key, value);
    } catch (Exception e) {
      log.warn("could not set property " + key + " on object " + object + ": " + e);
    }
  }
}